/**
	This file is part of Fighting Field.

	Fighting Field is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	Fighting Field is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with Fighting Field.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef FF_STATEMACHINE_H
#define FF_STATEMACHINE_H

#include "Prerequisites.h"

namespace FF
{
	class State;
}

namespace FF
{
	struct StateParam {};
	const StateParam BLANK_STATE_PARAM;

	class StateMachine
	{
	private:
		enum Action
		{
			Push, Pop, PopAll, Change
		};

	public:
		StateMachine();
		virtual ~StateMachine();

	public:
		virtual bool Step(const Ogre::Real& dt);
		virtual bool Update(const Ogre::Real& dt);

		// Manage states
		State* GetState(unsigned char level = 0) const;

		template<class T>
		bool IsState(unsigned char level = 0) const
		{
			State* state = GetState(level);
			if (!state)
				return false;
			else
				return dynamic_cast<T*>(state) != NULL;
		}

		template<class T>
		bool PushState(const StateParam& param = BLANK_STATE_PARAM)
		{
			mActions.push_back(std::pair<Action, std::pair<State*, StateParam> >(Push, std::pair<State*, StateParam>(new T(this), param)));
			return true;
		}

		template<class T>
		bool ChangeState(const StateParam& param = BLANK_STATE_PARAM)
		{
			mActions.push_back(std::pair<Action, std::pair<State*, StateParam> >(Change, std::pair<State*, StateParam>(new T(this), param)));
			return true;
		}

		void PopState(const StateParam& param = BLANK_STATE_PARAM);
		void PopAllStates(const StateParam& param = BLANK_STATE_PARAM);

		template<class T>
		bool PushStateNow(const StateParam& param = BLANK_STATE_PARAM)
		{
			return ExecuteAction(Push, new T(this));
		}

		template<class T>
		bool ChangeStateNow(const StateParam& param = BLANK_STATE_PARAM)
		{
			return ExecuteAction(Change, new T(this));
		}

		void PopStateNow(const StateParam& param = BLANK_STATE_PARAM);
		void PopAllStatesNow(const StateParam& param = BLANK_STATE_PARAM);

	private:
		void ExecuteAllActions();
		bool ExecuteAction(Action action, State* state = NULL, const StateParam& param = BLANK_STATE_PARAM);

	protected:
		State* mCurrentState;
		std::vector<State*> mStates;
		std::vector< std::pair<Action, std::pair<State*, StateParam> > > mActions;
	};
}

#endif